package com.example.statemachine.support;

import com.example.statemachine.core.EventEnum;
import com.example.statemachine.core.StateEnum;
import com.example.statemachine.core.StateMachineException;
import com.example.statemachine.core.TransitionRequest;
import com.example.statemachine.core.TransitionResult;
import com.example.statemachine.support.annotation.StateMachine;
import com.example.statemachine.support.annotation.StateTransition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class StateTransitionProcessor {

    @Autowired
    private ApplicationContext applicationContext;

    private final Map<String, Map<StateEventKey, Method>> transitionMethods = new ConcurrentHashMap<>();

    public <T> TransitionResult<T> processTransition(T entity, TransitionRequest<T> request, Class<T> entityClass) {
        String machineKey = getMachineKey(entityClass);
        StateEventKey key = new StateEventKey(request.getCurrentState(), request.getEvent());

        log.debug("查找状态转换方法: machineKey={}, source={}, event={}",
                machineKey, request.getCurrentState(), request.getEvent());

        // 打印所有已注册的转换方法
        logRegisteredTransitions(machineKey);

        // 查找对应的转换方法
        Method transitionMethod = findTransitionMethod(machineKey, key);
        if (transitionMethod == null) {
            // 打印所有可用的转换方法以便调试
            logAvailableTransitions(machineKey, request.getCurrentState());
            throw new StateMachineException(
                    String.format("未找到从状态[%s]通过事件[%s]的转换方法",
                            request.getCurrentState(), request.getEvent()));
        }

        // 设置目标状态
        StateTransition annotation = transitionMethod.getAnnotation(StateTransition.class);
        request.setTargetState(annotation.target());

        // 执行转换方法
        return executeTransitionMethod(entity, transitionMethod, request);
    }

    private void logRegisteredTransitions(String machineKey) {
        Map<StateEventKey, Method> transitions = transitionMethods.get(machineKey);
        if (transitions != null) {
            log.debug("已注册的转换方法 (machineKey: {}):", machineKey);
            for (Map.Entry<StateEventKey, Method> entry : transitions.entrySet()) {
                StateTransition annotation = entry.getValue().getAnnotation(StateTransition.class);
                log.debug("  {} -> {} -> {}",
                        annotation.source(), annotation.event(), annotation.target());
            }
        } else {
            log.debug("没有找到machineKey为 {} 的注册转换方法", machineKey);
        }
    }

    private void logAvailableTransitions(String machineKey, StateEnum currentState) {
        Map<StateEventKey, Method> transitions = transitionMethods.get(machineKey);
        if (transitions != null) {
            log.debug("从状态 {} 可用的转换:", currentState);
            for (Map.Entry<StateEventKey, Method> entry : transitions.entrySet()) {
                if (entry.getKey().source == currentState) {
                    StateTransition annotation = entry.getValue().getAnnotation(StateTransition.class);
                    log.debug("  事件: {} -> 目标状态: {}", annotation.event(), annotation.target());
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    private <T> TransitionResult<T> executeTransitionMethod(T entity, Method method, TransitionRequest<T> request) {
        try {
            Object bean = applicationContext.getBean(method.getDeclaringClass());

            log.debug("执行状态转换方法: {}.{}",
                    method.getDeclaringClass().getSimpleName(), method.getName());

            // 调用转换方法
            Object result = ReflectionUtils.invokeMethod(method, bean, entity, request.getContext());

            T updatedEntity = (T) result;

            return new TransitionResult<>(updatedEntity, request.getCurrentState(),
                    request.getTargetState(), request.getContext());

        } catch (Exception e) {
            log.error("执行状态转换方法失败: {}", method.getName(), e);
            throw new StateMachineException("状态转换执行失败: " + e.getMessage(), e);
        }
    }

    private Method findTransitionMethod(String machineKey, StateEventKey key) {
        Map<StateEventKey, Method> machineTransitions = transitionMethods.get(machineKey);
        if (machineTransitions == null) {
            return null;
        }
        return machineTransitions.get(key);
    }

    public void registerTransitionMethod(Class<?> clazz, Method method, StateTransition annotation) {
        String machineKey = getMachineKey(clazz);
        StateEventKey key = new StateEventKey(annotation.source(), annotation.event());

        transitionMethods
                .computeIfAbsent(machineKey, k -> new HashMap<>())
                .put(key, method);

        log.info("注册状态转换方法: {} -> {} -> {} (machineKey: {})",
                annotation.source(), annotation.event(), annotation.target(), machineKey);
    }

    private String getMachineKey(Class<?> clazz) {
        StateMachine stateMachineAnnotation = AnnotationUtils.findAnnotation(clazz, StateMachine.class);
        if (stateMachineAnnotation != null) {
            // 使用实体类型的类名作为machineKey
            return stateMachineAnnotation.entityType().getSimpleName();
        }
        return clazz.getSimpleName();
    }

    private static class StateEventKey {
        private final StateEnum source;
        private final EventEnum event;

        public StateEventKey(StateEnum source, EventEnum event) {
            this.source = source;
            this.event = event;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            StateEventKey that = (StateEventKey) o;
            return source == that.source && event == that.event;
        }

        @Override
        public int hashCode() {
            return Objects.hash(source, event);
        }

        @Override
        public String toString() {
            return source + "->" + event;
        }
    }
}